package System_code.MyBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ling
 * Date: 2022-08-06
 * Time: 15:41
 */
// 自己模拟实现一个阻塞队列.
// 基于数组的方式来实现队列.
// 提供两个核心方法:
// 1. put 入队列
// 2. take 出队列
public class MyBlockingQueue {
    // 假定最大是 1000 个元素. 当然也可以设定成可配置的.
    private int[] items = new int[1000];
    private final int MAX_SIZE=1000;
    // 队首的位置
    private int head = 0;
    // 队尾的位置
    private int tail = 0;
    // 队列的元素个数
    volatile private int size = 0;

    // 入队列
    public void put(int val) throws InterruptedException {
        synchronized (this){
            while((tail+1)%MAX_SIZE==head){//用while不用if的原因是为了能在等待之前判定一次，唤醒后，再确认一次（这也是Java官方文档建议的做法）
                //队列已满，无法插入
                this.wait();
            }
            items[tail]=val;
            tail=(tail+1)%MAX_SIZE;
            size++;
            // 即使没人在等待, 多调用几次 notify 也没啥副作用~~
            this.notify();
        }
    }
    // 出队列
    public Integer take() throws InterruptedException {
        int ret=0;
        synchronized (this){
            while(size==0){//用while不用if的原因是为了能在等待之前判定一次，唤醒后，再确认一次（这也是Java官方文档建议的做法）
                //队列为空，阻塞等待
                this.wait();
            }
            ret=items[head];
            head=(head+1)%MAX_SIZE;
            size--;
            this.notify();

        }
        return ret;
    }
}
/**
 * 这里讲一下代码，假设我们创建了MyBlockingQueue queue=new MyBlockingQueue(),这个阻塞队列，然后我们创建两个线程Thread t1,t2,
 * 线程t1对queue进行出队和入队，线程t2对queue进行入队和出队，我们假设系统调度先执行t1,那么t1执行调用take的任务，则t1拿到对象锁，
 * 则t2拿不到锁，然后t1执行take，发现queue为空，则调用this.wait(),wait将t1拿到的锁释放,等待唤醒，然后由于t1阻塞等待，系统调度t2拿到对象锁
 * t2执行入队，然后唤醒t1，如果t2里的任务中的put方法执行完毕（但是t2整个任务没执行完（Runnable(入队和出队，入队执行完了)）），
 * 此时t1,t2由系统调度看先执行哪一个，假设系统调度t1，那么t1开始出队，以此类推
 *
 */
